/** File name: funcCA.cu 
*** Author: Tran Van Hoang
*** Date: 05/04/2015
**/

#include <stdio.h>
#include <math.h>
#include "cellNetwork.h"

#define MAX_NUM_MOVING 50

/***************************************************************/
/********************* FOREST FEDERATE *************************/
/***************************************************************/

//Randomly firing some places
void initTreeState(TreeState *node_state, int node_number, int num_fired_node)
{
	int node;

	int firedNode[num_fired_node];
	
	for (int i = 0; i < node_number; i++)
	{
	    node_state[i].treeState = NORMAL;
	}

	for (int i = 0; i < num_fired_node; i++)
	{
	    firedNode[i] = -1;
	}

	srand(time(NULL));

	for (int i = 0; i < num_fired_node; i++)
	{

	    while(1)
	    {
		bool fired = false;
	    	node = rand() % node_number;
		for (int j = 0; j < i; j++)
		{
		    if (firedNode[j] == node)
		    {
			fired = true;
			break;
		    }
		}
		if (fired == false)
	        {
		   firedNode[i] = node;
		   break;
		}
	    }
	    node_state[node].treeState = FIRED;
	}
}

//-------------------------------------------------------------------------------------------
/**   Version 1.0
***** One cell fired if one of its neighbor is fired. If it fired, it changes to ash. If it is ash, it will become empty.
***** 			
*/

__device__ TreeState computeStateForest(TreeState *nowState_d, int nodeIndex, canaux *channels_d, curandState* devState_d)
{
	TreeState myState;

	myState = nowState_d[nodeIndex];

	//Checking its neighbours
	int nbIn = channels_d[nodeIndex].nbIn;

	if (myState.treeState == NORMAL)
	{
	   int nodeIn;
	   for (int i = 0; i < nbIn; i++)
	   {
     	    	nodeIn = channels_d[nodeIndex].read[i].node;

	    	if (nowState_d[nodeIn].treeState == FIRED)
	    	{
		    myState.treeState = FIRED;
		    break;
	    	}
	   }
	}
	else if (myState.treeState == FIRED)
	{
	   myState.treeState = ASH;
	}else if (myState.treeState == ASH)
	{
	   myState.treeState = EMPTY;
	}
	return myState;
}

/**
*This function the changing the state of each cell of the  grid
*
*/
__global__ void stepStateForest(TreeState *nowState_d, TreeState *nextState_d, canaux *channels_d, int node_number, curandState *devStates)
{

	int idx = threadIdx.x + blockIdx.x * blockDim.x;

	if (idx < node_number)
	{
	    nextState_d[idx] = computeStateForest(nowState_d, idx, channels_d, devStates);
	}
}

/**************************Cellular Automata****************************/

/**
* This function checks whether the states of nodes of system can be changed or not. If there are no changed, simulation will be stoped.
*/
__global__ void IsChanged(NodeState *states)
{

}
//------------------------------------------------------------------------------------
/**Random generator
****/
__global__ void setup_kernel ( curandState * state, unsigned long seed )
{
    int id = threadIdx.x;
    curand_init ( seed, id, 0, &state[id] );
} 

__device__ float generateNumber(curandState* globalState, int nodeIndex)
{
    curandState localState = globalState[nodeIndex];
    float random = curand_uniform( &localState );
    globalState[nodeIndex] = localState; 
    return random;
}
/**
* This function initilizes the values of node in cell network. In this case, it randomly creates 2 polluted points in the river.
* Each node has its density with the value in the range 0-MAX_POLLUTION_DENSITY showing the density of pollution in the river. 
* If value is 0, that node is clean.
*/
void initState(NodeState *state, int node_number)
{
	int node;
	for (int i = 0; i < node_number; i++)
	{
	    state[i].density = 0.0;
	    //state[i].isChanged = true;
	}
	//Radomly creating two polluted points with a certain density
	srand(time(NULL));

	node = rand() % node_number;//Node1
	state[500].density = 10.0;

	node = rand() % node_number;//Node2
	state[300].density = 8.0;

	node = rand() % node_number;//Node3
	state[58].density = 10.0;

	node = rand() % node_number;//Node4
	state[450].density = 10.0;


}
/** This function allows to create the source of pollution with the different corverages.
*/
void initState_v1(NodeState *node_state, int node_number, int num_polluted_node)
{
	int node;

	int pollutedNode[num_polluted_node];
	
	for (int i = 0; i < node_number; i++)
	{
	    node_state[i].density = 0.0;
	}

	for (int i = 0; i < num_polluted_node; i++)
	{
	    pollutedNode[i] = -1;
	}

	srand(time(NULL));

	for (int i = 0; i < num_polluted_node; i++)
	{

	    while(1)
	    {
		bool polluted = false;
	    	node = rand() % node_number;
		for (int j = 0; j < i; j++)
		{
		    if (pollutedNode[j] == node)
		    {
			polluted = true;
			break;
		    }
		}
		if (polluted == false)
	        {
		   pollutedNode[i] = node;
		   break;
		}
	    }
	    node_state[node].density = rand() % MAX_POLLUTION_DENSITY;
	    //printf("node %d: %.2f\n", node, node_state[node].density);
	}
}

/*
__global__ void initStateCricket(NodeState *node_state_d, curandState *devStates_d, int node_number)
{
	/////////////////// Initilizing Insects////////////////////
	int idx = threadIdx.x + blockIdx.x * blockDim.x;

	if (idx < node_number)
	{
	    int nbInsects = (int) (generateNumber(devStates_d, idx) * (float) MAX_NUM_CRICKET);
	    node_state_d[idx].nbCrickets = nbInsects;
	    node_state_d[idx].messageList.limit = 0;
	}
}

void initStateCricket(NodeState *state, int number)
{
	/////////////////// Initilizing Cricket////////////////////
	int randNum;
	for (int i = 0; i < number; i++)
	{
	    randNum =  rand() % MAX_NUM_CRICKET;
	    state[i].nbCrickets = randNum > 80  ? randNum : 0; //try to create some regions instead of cover the whole.	    
	    state[i].messageList.limit = 0;//There are no message for initilizing.
	}
}
*/
//----------------------------------------------------------------------------------------
/**** Cricket transition rules
***** This function defines transition rules for crickets movement:
*/

/*
//Defining rule for producing new insects
__device__ int producing(int origin, int nodeIndex, curandState *devStates_d)
{
	int num_producing = 0;
	float randNum = generateNumber(devStates_d, nodeIndex);
	num_producing = (int) (randNum * (float) origin)/2;
	return num_producing;
}

__device__ void ResetMessage(NodeState *node, int nodeIndex)
{
	node[nodeIndex].messageList.limit = 0;
}

__device__ void AddMessageToNode(Message msg, NodeState *node, int nodeIndex)
{
	int limit;

	limit = node[nodeIndex].messageList.limit;
	node[nodeIndex].messageList.message[limit] = msg;
	node[nodeIndex].messageList.limit++;
}


__device__ NodeState computeStateCricket(NodeState *nowState_d, int nodeIndex, canaux *channels_d, curandState *devStates_d)
{
	NodeState myState;

	myState = nowState_d[nodeIndex];
	
	float sendingPercent = generateNumber(devStates_d, nodeIndex);//number between 0.0 -> 1.0

	int nbIn = channels_d[nodeIndex].nbIn;

	int nodeIn;

	int sending = (int) ((float) myState.nbCrickets * sendingPercent);

	//If there is only one cricket. It will move to other place in case of random percentage > 50
	if (myState.nbCrickets == 1 && sendingPercent > 50)
	{
	    sending = 1;
	}

	if (sending > MAX_NUM_MOVING)
	{
	    sending = MAX_NUM_MOVING;
	}
	
	//Distributing this sending to its neighbor according to the amount of percentage of crickets of each among them.
	int sumCricketsOfNeighbor = 0;
	
	for (int i = 0; i < nbIn; i++)
	{
     	    nodeIn = channels_d[nodeIndex].read[i].node;
	    sumCricketsOfNeighbor = sumCricketsOfNeighbor + nowState_d[nodeIn].nbCrickets;
	}
	
	//Sending message to its neighbors
	Message msg;
	int sumSending = 0;
	for (int i = 0; i < nbIn; i++)
	{
     	    nodeIn = channels_d[nodeIndex].read[i].node;
	    if (sending == 0) {break;}
	    if (sending == 1)
	    {
		msg.nbCrickets = 1;
		AddMessageToNode(msg, nowState_d, nodeIn);
		sumSending = sumSending + 1;
		break;
	    }

	    msg.nbCrickets = (int) (((float)nowState_d[nodeIn].nbCrickets / (float)sumCricketsOfNeighbor) * (float) sending);
	    sumSending = sumSending + msg.nbCrickets;	    
	    AddMessageToNode(msg, nowState_d, nodeIn);
	}

	//Substracting 
	myState.nbCrickets = myState.nbCrickets - sumSending;
	//Adding new crickets
	myState.nbCrickets = myState.nbCrickets + producing(myState.nbCrickets, nodeIndex, devStates_d);
	//myState.nbCrickets = sumSending;
	return myState;
}
*/
//-----------------------------------------------------------------------------------------
/** This function allows to update new value for node state depending on messages received.
***/
/*
__global__ void updateNodeState(NodeState *nowState_d, NodeState *nextState_d, int node_number)
{
	int idx = threadIdx.x + blockIdx.x * blockDim.x;

	if (idx < node_number)
	{
	    NodeState thisState;

	    thisState = nowState_d[idx];
	    int sum = 0;
	    for (int i = 0; i < thisState.messageList.limit; i++)
	    {
		sum = sum + thisState.messageList.message[i].nbCrickets;
	    }
	    nextState_d[idx].nbCrickets = nextState_d[idx].nbCrickets + sum;
	    //nextState_d[idx].nbCrickets = sum;
	    ResetMessage(nowState_d, idx);
	}
}
*/
//-------------------------------------------------------------------------------------------
/**   Version 1.0
***** This function defines transition rule for pollution diffusion:
***** A simple rule: At each cell, its density will be reduced a half, which will be transported 
***** to its neighbor with an amount calculated by the formular: the half / the number of neighbour. 
***** And then, it also receives an amount of density from its neighbour.
***** 			
*/

__device__ NodeState computeState(NodeState *nowState_d, int nodeIndex, 
					canaux *channels_d, curandState* devState_d)
{
	NodeState myState;
	int nbIn;

	/// Getting pollution density of the cell
	myState = nowState_d[nodeIndex];
	/// Getting neighbours of the cell
	nbIn = channels_d[nodeIndex].nbIn;

	int nodeIn;
	float receive = 0;
	for (int i = 0; i < nbIn; i++)
	{
	    /// Getting id of the neighbours
     	    nodeIn = channels_d[nodeIndex].read[i].node;
	    receive = receive + ((nowState_d[nodeIn].density / 2.0) / (float) channels_d[nodeIn].nbIn);
	}
	/// Computing the new state
	myState.density = (myState.density / 2.0) + receive;

	return myState;
}
//-------------------------------------------------------------------------------------------
/**   Version 1.0_CPU
***** This function defines transition rule for pollution diffusion:
***** A simple rule: At each cell, its density will be reduced a half, which will be transported 
***** to its neighbor with an amount calculated by the formular: the half / the number of neighbour. 
***** And then, it also receives an amount of density from its neighbour.
***** 			
*/

NodeState computeState_h(NodeState *nowState_h, int nodeIndex, canaux *channels_h)
{	
	NodeState myState;
	
	myState = nowState_h[nodeIndex];

	//Checking its neighbours
	int nbIn = channels_h[nodeIndex].nbIn;

	int nodeIn;

	float receive = 0;

	for (int i = 0; i < nbIn; i++)
	{
     	    nodeIn = channels_h[nodeIndex].read[i].node;
	    receive = receive + ((nowState_h[nodeIn].density / 2.0) / (float) channels_h[nodeIn].nbIn);
	}

	myState.density = (int) (myState.density / 2.0) + receive;

	return myState;
}

NodeState computeState_h_v1(NodeState *nowState_h, int nodeIndex, canaux *channels_h)
{
	NodeState myState;
	float lossPercentage, receive, loss;
	int nodeIn, nbIn;

	myState = nowState_h[nodeIndex];
	
	srand(time(NULL));
	//Checking its neighbours
	nbIn = channels_h[nodeIndex].nbIn;
	lossPercentage = rand() % 10; 
	loss = (lossPercentage * myState.density) / 100.0;
	receive = 0;

	for (int i = 0; i < nbIn; i++)
	{
     	    nodeIn = channels_h[nodeIndex].read[i].node;
	    receive = receive + ((nowState_h[nodeIn].density / 2.0) / (float) channels_h[nodeIn].nbIn);
	}

	myState.density = (myState.density / 2.0) + receive - loss;
	if (myState.density < 0.0)
	{
	    myState.density = 0.0;
	}
	return myState;
}


//-------------------------------------------------------------------------------------------
/**   Version 1.1
***** This function defines transition rule for pollution diffusion.
***** A simple rule: At each cell, its density will be reduced a half, which will be transported 
***** to its neighbor with an amount calculated by the formular: the half / the number of neighbour. 
***** And then, it also receives an amount of density from its neighbour.
***** Added: At every step, the imitting will be randomly taken place at cells having the density, which is lagrer than zero.
***** 			
*/

__device__ NodeState computeState_v1(NodeState *nowState_d, int nodeIndex, canaux *channels_d, curandState *devStates_d)
{
	NodeState myState;
	float lossPercentage, loss;
	int nbIn, nodeIn;

	myState = nowState_d[nodeIndex];	
	/// Generating a random value in [0.0 - 1.0]
	lossPercentage = generateNumber(devStates_d, nodeIndex);
	/// Calculating an amount of loss.
	loss = lossPercentage * myState.density;
	/// Finding number of neighbour
	nbIn = channels_d[nodeIndex].nbIn;

	float receive = 0;

	for (int i = 0; i < nbIn; i++)
	{
	    /// Reading id of neighbour
     	    nodeIn = channels_d[nodeIndex].read[i].node;
	    receive = receive + ((nowState_d[nodeIn].density / 2.0) / 
				(float) channels_d[nodeIn].nbIn);
	}

	myState.density = (myState.density / 2.0) + receive - loss;

	if (myState.density < 0.0) 
	{
	   myState.density = 0.0;
	}
	return myState;
}

/** Cricket version 1.0
*This function the changing the state of each cell of the  grid
*
*/
/*
__global__ void stepStateCricket(NodeState *nowState_d, NodeState *nextState_d, canaux *channels_d, int node_number, curandState *devStates_d)
{

	int idx = threadIdx.x + blockIdx.x * blockDim.x;

	if (idx < node_number)
	{
	    nextState_d[idx] = computeStateCricket(nowState_d, idx, channels_d, devStates_d);
	}
}
*/

/** River version 1.0
*This function the changing the state of each cell of the  grid
*
*/
__global__ void stepState(NodeState *nowState_d, NodeState *nextState_d, canaux *channels_d, int node_number, curandState *devStates)
{

	int idx = threadIdx.x + blockIdx.x * blockDim.x;

	if (idx < node_number)
	{
	    nextState_d[idx] = computeState(nowState_d, idx, channels_d, devStates);
	}
}

/** River version 1.0_CPU
*/
void stepState_h(NodeState *nowState_h, NodeState *nextState_h, canaux *channels_h, int node_number)
{

	for (int i = 0; i < node_number; i++)
	{
	    nextState_h[i] = computeState_h_v1(nowState_h, i, channels_h);
	}
}

/*******************Game Of Life************************/
void initNodeState(int *now_h, int *next_h, int node_number)
{
	for (int i = 0; i < node_number; i++)
	{
		//now_h[i] = rand()%2;
		now_h[i] = 1;
		next_h[i] = -1;
	}
	
//	now_h[31] = 0;
//	now_h[104] = 0;
//	now_h[122] = 0;
//	now_h[77] = 0;	
//	now_h[179] = 0;
//	now_h[66] = 0;
//	now_h[29] = 0;
//	now_h[16] = 0;
}

__device__ int doSum(int node, canaux channels[], int now_d[])
{
	int sum = 0;

	for (int i = 0; i < channels[node].nbOut; i++)
	{
		sum = sum + now_d[channels[node].read[i].node];			
	}

	return sum;
}

int doSum_h(int node, canaux channels[], int s_h[])
{
	int sum = 0;
	for (int i = 0; i < channels[node].nbOut; i++)
	{
		sum = sum + s_h[channels[node].read[i].node];
	}	
	return sum;
}

__device__ int computeLife(int state, int sum)
{
	int result;
	if (state == 1)
	{
		if (sum > 3 || sum < 2)		
		    result = 0;		
		else 
		    result = 1;
	}

	if (state == 0)
	{
		if (sum == 3)
		    result = 1;
		else 
		    result = 0;
	}
	return result;
}

//Cellular Automata
__global__ void changeState(int *now_d, int *next_d, canaux channels[], int node_number)
{

	int index = threadIdx.x + blockIdx.x * blockDim.x;

	if (index < node_number)
	{
	    next_d[index] = computeLife(now_d[index], doSum(index, channels, now_d));
	}
}
